package net.sk.china.front.strategy;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.model.user.User;
import net.sk.china.common.model.user.UserSmsRecord;
import net.sk.china.common.utils.DateUtil;
import net.sk.china.common.utils.RedisUtil;
import net.sk.china.common.utils.TalkUtil;
import net.sk.china.front.constants.Constants;
import net.sk.china.front.request.LoginRequest;
import net.sk.china.front.request.SendCodeRequest;
import net.sk.china.front.response.LoginResponse;
import net.sk.china.front.response.SmsResponse;
import net.sk.china.front.service.system.SystemConfigService;
import net.sk.china.front.service.user.UserService;
import net.sk.china.front.service.user.UserSmsRecordService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.TimeUnit;

/**
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2018-2024 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2024/6/5 15:37
 * // +----------------------------------------------------------------------
 */
@Component
public class SmsService implements UserInterface {

    private static final Logger logger = LoggerFactory.getLogger(SmsService.class);

    private final UserService userService;

    private final RedisUtil redisUtil;

    private final UserSmsRecordService userSmsRecordService;

    private final RestTemplate restTemplate;

    private final SystemConfigService systemConfigService;

    public SmsService(UserService userService, RedisUtil redisUtil, UserSmsRecordService userSmsRecordService, RestTemplate restTemplate, SystemConfigService systemConfigService) {
        this.userService = userService;
        this.redisUtil = redisUtil;
        this.userSmsRecordService = userSmsRecordService;
        this.restTemplate = restTemplate;
        this.systemConfigService = systemConfigService;
    }

    /**
     * 手机号验证码登录
     * @param request 请求对象
     * @param ip 客户端IP
     * @return {@code LoginResponse}
     */
    @Override
    public LoginResponse login(LoginRequest request, String ip) {
        if (StrUtil.isBlank(request.getCaptcha())) {
            throw new TalkException("请输入验证码");
        }
        checkValidateCode(request.getPhone(), "login", request.getCaptcha());

        //查询手机号信息
        User user = userService.getByAccount(request.getPhone());
        LoginResponse loginResponse = new LoginResponse();
        if (ObjectUtil.isNull(user)) {
            // 此用户不存在，走新用户注册流程
            user = userService.registerPhone(request.getPhone(), request.getLoginType(), ip);
            loginResponse.setType("register");
        } else {
            if (user.getStatus().equals(Constants.NUM_ZERO)) {
                throw new TalkException("当前账户已禁用，请联系平台客服人员！");
            }

            // 记录最后一次登录信息
            user.setLastLoginTime(DateUtil.getNowTime());
            user.setLoginType(request.getLoginType());
            user.setLastIp(ip);
            if (!userService.updateById(user)) {
                logger.error("用户登录时，记录最后一次登录时间出错,uid = " + user.getUid());
            }
            loginResponse.setType("login");
        }

        // 生成token
        try {
            redisUtil.delete(Constants.SMS_VALIDATE_PHONE + "login" + ":" +  request.getPhone());
            String token = userService.createToken(user.getUid());
            loginResponse.setToken(token);
        } catch (Exception e) {
            logger.error(String.format("生成用户Token失败: %s", e.getMessage()));
        }

        return loginResponse;
    }


    /**
     * 获取验证码
     * @param request 请求对象
     * @param ip 客户端IP
     * @return {@code boolean}
     */
    public boolean sendCode(SendCodeRequest request, String ip) {

        if (request.getType().equals(Constants.RETRIEVE) || request.getType().equals(Constants.PC_RETRIEVE)) {
            User user = userService.getByAccount(request.getPhone());
            if (ObjectUtil.isNull(user)) {
                throw new TalkException("该手机号还未注册过，无法进行找回密码操作");
            }
        }

        if (request.getType().equals(Constants.REGISTER) || request.getType().equals(Constants.PC_REGISTER)) {
            User user = userService.getByAccount(request.getPhone());
            if (ObjectUtil.isNotNull(user)) {
                throw new TalkException("您已注册，请勿重新注册");
            }
        }

        String redis = Constants.SMS_VALIDATE_PHONE + request.getType() + ":" + request.getPhone();
        boolean exists = redisUtil.exists(redis);
        if (exists) {
            throw new TalkException("请勿频繁获取验证码");
        }

        String time = DateUtil.localDateTimeToString(DateUtil.getNowTime(), "yyyy-MM-dd");
        long count = userSmsRecordService.getCount(request.getPhone(), time);
        int maxCount = Integer.parseInt(systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_SMS_CODE_COUNT));
        if (maxCount <= count) {
            throw new TalkException("您已经超过每日短信阈值：" + maxCount + "条");
        }

        Integer code = TalkUtil.randomCount(10000,99999);
        try {
            UserSmsRecord sms = new UserSmsRecord();
            sms.setPhone(request.getPhone());
            sms.setAddIp(ip);
            sms.setType(request.getType());
            sms.setCreateTime(DateUtil.getNowTime());

            String result;
            String content = StrUtil.format(systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_SMS_CODE_TEMPLATE), code);
            String url = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_SMS_CODE_URL);

            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
            params.add("text", content);
            params.add("apikey", systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_SMS_CODE_APIKEY));
            params.add("mobile", request.getPhone());

            result = postFormData(url, params);
            SmsResponse response = JSON.parseObject(result, SmsResponse.class);
            if (response.getCode() != 0) {
                throw new TalkException(response.getMsg());
            }

            sms.setContent(content);
            sms.setSid(String.valueOf(response.getSid()));

            // 添加短信记录
            sms.setMemo(result);
            userSmsRecordService.save(sms);


            // PC端注册时需要填写很多材料，先设置20分钟后过期
            redisUtil.set(redis, code, 20L, TimeUnit.MINUTES);
        } catch (Exception e) {
            throw new TalkException(String.format("短信发送失败：%s", e.getMessage()));
        }

        return true;
    }

    /**
     * 发送POST请求
     * @param url 接口地址
     * @param map 请求参数
     * @return {@code String}
     */
    private String postFormData(String url, MultiValueMap<String, String> map) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.add("Accept",MediaType.TEXT_PLAIN_VALUE);
        HttpEntity<MultiValueMap<String, String>> requests = new HttpEntity<MultiValueMap<String, String>>(map, headers);

        return restTemplate.postForEntity(url, requests, String.class).getBody();
    }

    /**
     * 检测手机验证码
     * @param phone 手机号
     * @param type 类型 login:登录 register:注册 retrieve:找回密码
     * @param code  验证码
     */
    public void checkValidateCode(String phone, String type, String code) {
        String redis = Constants.SMS_VALIDATE_PHONE + type + ":" +  phone;
        Object validateCode = redisUtil.get(redis);
        if (ObjectUtil.isNull(validateCode)) {
            throw new TalkException("验证码已过期或已使用,请重新获取");
        }
        if (!validateCode.toString().equals(code)) {
            throw new TalkException("验证码错误");
        }
        //删除验证码
        //redisUtil.delete(redis);
    }
}
